#include "gpios.h"

// Pin reservation tracking
static uint8_t reserved_pins = 0;  // Bitmask for reserved pins
static const char* pin_owners[8] = {0}; // Track which library owns each pin

// Convert pin to bit position for reservation tracking
static uint8_t pinToBit(uint8_t pin) {
    switch(pin) {
        case PA1: return 0;
        case PA2: return 1; 
        case PC1: return 2;
        case PC2: return 3;
        case PC4: return 4;
        case PD4: return 5;
        default: return 7; // Invalid
    }
}

// Pin reservation functions
uint8_t reservePin(uint8_t pin, const char* library_name) {
    if (!isValidPin(pin)) return GPIO_INVALID_PIN;
    
    uint8_t bit = pinToBit(pin);
    if (bit == 7) return GPIO_INVALID_PIN;
    
    if (reserved_pins & (1 << bit)) {
        return GPIO_PIN_RESERVED; // Already reserved
    }
    
    reserved_pins |= (1 << bit);
    pin_owners[bit] = library_name;
    return GPIO_OK;
}

uint8_t releasePin(uint8_t pin) {
    if (!isValidPin(pin)) return GPIO_INVALID_PIN;
    
    uint8_t bit = pinToBit(pin);
    if (bit == 7) return GPIO_INVALID_PIN;
    
    reserved_pins &= ~(1 << bit);
    pin_owners[bit] = 0;
    return GPIO_OK;
}

uint8_t isPinReserved(uint8_t pin) {
    if (!isValidPin(pin)) return 0;
    
    uint8_t bit = pinToBit(pin);
    if (bit == 7) return 0;
    
    return (reserved_pins & (1 << bit)) ? 1 : 0;
}

uint8_t isPinReservedBy(uint8_t pin, const char* library_name) {
    if (!isValidPin(pin) || !library_name) return 0;
    
    uint8_t bit = pinToBit(pin);
    if (bit == 7) return 0;
    
    // Check if pin is reserved and by the specified library
    if (!(reserved_pins & (1 << bit))) return 0;  // Not reserved at all
    
    if (!pin_owners[bit]) return 0;  // No owner recorded
    
    // Compare library names (simple string comparison)
    const char* owner = pin_owners[bit];
    const char* requester = library_name;
    
    // Simple string comparison
    while (*owner && *requester && (*owner == *requester)) {
        owner++;
        requester++;
    }
    
    return (*owner == *requester) ? 1 : 0;  // True if strings match
}

// Helper functions for pin validation
uint8_t isValidPin(uint8_t pin) {
    return (pin == PA1 || pin == PA2 || pin == PC1 || 
            pin == PC2 || pin == PC4 || pin == PD4);
}

uint8_t isADCPin(uint8_t pin) {
    return (pin == PA1 || pin == PD6 ||  // Physical pin 1 (same pin, different names)
            pin == PA2 ||                // Physical pin 3
            pin == PC4 ||                // Physical pin 7
            pin == PD4);                 // Physical pin 8
}

uint8_t isPWMPin(uint8_t pin) {
    return (pin == PC1 ||  // PIN_5 - TIM2_CH4
            pin == PC2 ||  // PIN_6 - TIM2_CH2
            pin == PC4 ||  // PIN_7 - TIM1_CH4
            pin == PD4);   // PIN_8 - TIM2_CH1
}

void gpio_init(void) {
    // Initialize all GPIO functionality using framework function
    funGpioInitAll();
    
    // Initialize ADC subsystem for analog operations
    funAnalogInit();
}

uint8_t pinMode(uint8_t pin, uint8_t mode) {
    // Validate pin
    if (!isValidPin(pin)) {
        return GPIO_INVALID_PIN;
    }
    
    // Check if pin is reserved by another library
    if (isPinReserved(pin)) {
        return GPIO_PIN_RESERVED;
    }
    
    // Validate mode
    if (mode > INPUT_ANALOG) {
        return GPIO_INVALID_MODE;
    }
    
    switch(mode) {
        case INPUT:
            funPinMode(pin, GPIO_CFGLR_IN_FLOAT);
            break;
        case INPUT_PULLUP:
            funPinMode(pin, GPIO_CFGLR_IN_PUPD);
            funDigitalWrite(pin, FUN_HIGH);
            break;
        case INPUT_PULLDOWN:
            funPinMode(pin, GPIO_CFGLR_IN_PUPD);
            funDigitalWrite(pin, FUN_LOW);
            break;
        case OUTPUT:
            funPinMode(pin, GPIO_CFGLR_OUT_10Mhz_PP);
            break;
        case OUTPUT_OD:
            funPinMode(pin, GPIO_CFGLR_OUT_10Mhz_OD);
            break;
        case INPUT_ANALOG:
            // Validate pin supports ADC
            if (!isADCPin(pin)) {
                return GPIO_NO_ADC;
            }
            funPinMode(pin, GPIO_CFGLR_IN_ANALOG);
            break;
        default:
            return GPIO_INVALID_MODE;
    }
    
    return GPIO_OK;
}

uint8_t pinModeReserved(uint8_t pin, uint8_t mode, const char* library_name) {
    // Validate pin
    if (!isValidPin(pin)) {
        return GPIO_INVALID_PIN;
    }
    
    // Check if pin is reserved by this library
    if (!isPinReservedBy(pin, library_name)) {
        // Either not reserved, or reserved by another library
        if (isPinReserved(pin)) {
            return GPIO_PIN_RESERVED;  // Reserved by another library
        } else {
            return GPIO_INVALID_PIN;   // Not reserved at all
        }
    }
    
    // Validate mode
    if (mode > INPUT_ANALOG) {
        return GPIO_INVALID_MODE;
    }
    
    // Configure the pin (same logic as pinMode but bypass reservation check)
    switch(mode) {
        case INPUT:
            funPinMode(pin, GPIO_CFGLR_IN_FLOAT);
            break;
        case INPUT_PULLUP:
            funPinMode(pin, GPIO_CFGLR_IN_PUPD);
            funDigitalWrite(pin, FUN_HIGH);
            break;
        case INPUT_PULLDOWN:
            funPinMode(pin, GPIO_CFGLR_IN_PUPD);
            funDigitalWrite(pin, FUN_LOW);
            break;
        case OUTPUT:
            funPinMode(pin, GPIO_CFGLR_OUT_10Mhz_PP);
            break;
        case OUTPUT_OD:
            funPinMode(pin, GPIO_CFGLR_OUT_10Mhz_OD);
            break;
        case INPUT_ANALOG:
            // Validate pin supports ADC
            if (!isADCPin(pin)) {
                return GPIO_NO_ADC;
            }
            funPinMode(pin, GPIO_CFGLR_IN_ANALOG);
            break;
        default:
            return GPIO_INVALID_MODE;
    }
    
    return GPIO_OK;
}

uint8_t digitalRead(uint8_t pin) {
    if (!isValidPin(pin)) {
        return GPIO_INVALID_PIN;
    }
    return funDigitalRead(pin) ? HIGH : LOW;
}

uint8_t digitalWrite(uint8_t pin, uint8_t value) {
    if (!isValidPin(pin)) {
        return GPIO_INVALID_PIN;
    }
    funDigitalWrite(pin, (value == HIGH) ? FUN_HIGH : FUN_LOW);
    return GPIO_OK;
}

uint16_t analogRead(uint8_t pin) {
    // Validate pin supports ADC
    if (!isADCPin(pin)) {
        return 0;  // Invalid pin - return 0
    }
    
    // Map pin to ADC channel based on physical pin mapping
    uint8_t channel;
    switch(pin) {
        case PA1:                            // Physical pin 1 (primary name)
        case PD6: channel = ANALOG_1; break; // Physical pin 1 (alternate name) - ADC Channel 1
        case PA2: channel = ANALOG_0; break; // Physical pin 3 - ADC Channel 0
        case PC4: channel = ANALOG_2; break; // Physical pin 7 - ADC Channel 2
        case PD4: channel = ANALOG_7; break; // Physical pin 8 - ADC Channel 7
        default: return 0;  // Should never reach here due to validation above
    }
    return funAnalogRead(channel);
}

// PWM globals - track initialization state
static uint8_t tim1_initialized = 0;
static uint8_t tim2_initialized = 0;

uint8_t analogWrite(uint8_t pin, uint16_t value) {
    // Validate pin supports PWM
    if (!isPWMPin(pin)) {
        return GPIO_NO_PWM;
    }
    
    // Check if pin is reserved by another library
    if (isPinReserved(pin)) {
        return GPIO_PIN_RESERVED;
    }
    
    // Clamp value to 8-bit range (0-255)
    if (value > 255) value = 255;
    
    // Handle PWM based on pin (PIN_X constants are aliases for GPIO pins)
    switch(pin) {
        case PC4: // PIN_7 - TIM1_CH4
        {
            if (!tim1_initialized) {
                // Enable GPIOC and TIM1
                RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1;
                
                // PC4 is T1CH4, 10MHz Output alt func, push-pull
                GPIOC->CFGLR &= ~(0xf<<(4*4));
                GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4);
                
                // Reset TIM1 to init all regs
                RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
                RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
                
                // Set prescaler and period
                TIM1->PSC = 0x0000;    // No prescaler (48MHz)
                TIM1->ATRLR = 255;     // 8-bit PWM resolution
                
                // Reload immediately
                TIM1->SWEVGR |= TIM_UG;
                
                // Enable CH4 output, positive polarity
                TIM1->CCER |= TIM_CC4E;
                
                // CH4 Mode is output, PWM1 (OC4M = 110)
                TIM1->CHCTLR2 |= TIM_OC4M_2 | TIM_OC4M_1;
                
                // Enable TIM1 outputs (required for advanced timer)
                TIM1->BDTR |= TIM_MOE;
                
                // Enable TIM1
                TIM1->CTLR1 |= TIM_CEN;
                
                tim1_initialized = 1;
            }
            
            // Set duty cycle
            TIM1->CH4CVR = value;
            break;
        }
        
        case PC1: // PIN_5 - TIM2_CH4 (remap needed)
        {
            if (!tim2_initialized) {
                // Enable GPIOC and TIM2
                RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO;
                RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
                
                // Configure TIM2 remap to use PC1 as CH4
                AFIO->PCFR1 &= ~AFIO_PCFR1_TIM2_REMAP;
                AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_PARTIALREMAP1;
                
                // PC1 is T2CH4_, 10MHz Output alt func, push-pull  
                GPIOC->CFGLR &= ~(0xf<<(4*1));
                GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*1);
                
                // Reset TIM2 to init all regs
                RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
                RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
                
                // Set prescaler and period
                TIM2->PSC = 0x0000;    // No prescaler
                TIM2->ATRLR = 255;     // 8-bit PWM resolution
                
                // CH4 Mode is output, PWM1 (OC4M = 110)
                TIM2->CHCTLR2 |= TIM_OC4M_2 | TIM_OC4M_1 | TIM_OC4PE;
                
                // Enable auto-reload
                TIM2->CTLR1 |= TIM_ARPE;
                
                // Enable CH4 output
                TIM2->CCER |= TIM_CC4E;
                
                // Initialize counter
                TIM2->SWEVGR |= TIM_UG;
                
                // Enable TIM2
                TIM2->CTLR1 |= TIM_CEN;
                
                tim2_initialized = 1;
            }
            
            // Set duty cycle
            TIM2->CH4CVR = value;
            break;
        }
        
        case PC2: // PIN_6 - TIM2_CH2 (remap needed)
        {
            if (!tim2_initialized) {
                // Enable GPIOC and TIM2
                RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO;
                RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
                
                // Configure TIM2 remap to use PC2 as CH2
                AFIO->PCFR1 &= ~AFIO_PCFR1_TIM2_REMAP;
                AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_PARTIALREMAP1;
                
                // PC2 is T2CH2_, 10MHz Output alt func, push-pull
                GPIOC->CFGLR &= ~(0xf<<(4*2));
                GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*2);
                
                // Reset TIM2 to init all regs
                RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
                RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
                
                // Set prescaler and period
                TIM2->PSC = 0x0000;
                TIM2->ATRLR = 255;
                
                // CH2 Mode is output, PWM1
                TIM2->CHCTLR1 |= TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2PE;
                
                // Enable auto-reload
                TIM2->CTLR1 |= TIM_ARPE;
                
                // Enable CH2 output
                TIM2->CCER |= TIM_CC2E;
                
                // Initialize counter
                TIM2->SWEVGR |= TIM_UG;
                
                // Enable TIM2
                TIM2->CTLR1 |= TIM_CEN;
                
                tim2_initialized = 1;
            }
            
            // Set duty cycle
            TIM2->CH2CVR = value;
            break;
        }
        
        case PD4: // PIN_8 - TIM2_CH1 (default mapping)
        {
            if (!tim2_initialized) {
                // Enable GPIOD and TIM2
                RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
                RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
                
                // PD4 is T2CH1, 10MHz Output alt func, push-pull
                GPIOD->CFGLR &= ~(0xf<<(4*4));
                GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4);
                
                // Reset TIM2 to init all regs
                RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
                RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
                
                // Set prescaler and period
                TIM2->PSC = 0x0000;
                TIM2->ATRLR = 255;
                
                // CH1 Mode is output, PWM1
                TIM2->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1 | TIM_OC1PE;
                
                // Enable auto-reload
                TIM2->CTLR1 |= TIM_ARPE;
                
                // Enable CH1 output
                TIM2->CCER |= TIM_CC1E;
                
                // Initialize counter
                TIM2->SWEVGR |= TIM_UG;
                
                // Enable TIM2
                TIM2->CTLR1 |= TIM_CEN;
                
                tim2_initialized = 1;
            }
            
            // Set duty cycle
            TIM2->CH1CVR = value;
            break;
        }
        
        default:
            // Should never reach here due to validation above
            return GPIO_NO_PWM;
    }
    
    return GPIO_OK;
}